# 这里是一个专门的多线程下载模块
import time
import hashlib
import json
import os
import random
import requests

from Logging import *

version_type_dict = {"release": "正式版", "snapshot": "快照版"}


def search_new_version(version: str, mode: str):
    """
    查询指定版本的Minecraft，需要指定目标版本和模式。
    :param version: 目标版本的版本号。
    :param mode: 三选一：Mojang、BMCLAPI、MCBBS。
    :return: 查询成功，返回该版本的[0]版本号、[1]JSONurl、[2]版本类型、[3]最近更改日期、[4]正式发布日期，以元组形式返回。查询失败，返回None。
    """
    tag = random_num_tag()
    logger.Logger.debug(tag + "即将开始以" + mode + "方式查询" + version + "。")
    if mode == "Mojang":
        version_list_api = "http://launchermeta.mojang.com/mc/game/version_manifest.json"
    elif mode == "BMCLAPI":
        version_list_api = "https://bmclapi2.bangbang93.com/mc/game/version_manifest.json"
    elif mode == "MCBBS":
        version_list_api = "https://download.mcbbs.net/mc/game/version_manifest.json"
    else:
        logger.Logger.warning(tag + "未知下载源，查询取消。")
        return None

    logger.Logger.debug(tag + "下载源已确定。")

    version_list_back = requests.get(url=version_list_api, timeout=20)

    if version_list_back.status_code == 200:
        logger.Logger.debug(tag + "下载源在线可用。")
    else:
        logger.Logger.warning(tag + "下载源响应不正常，查询取消。")
        return None

    version_list_json = json.loads(version_list_back.text)
    version_list = version_list_json["versions"]

    temp_1 = {}
    for i in version_list:
        if i["id"] == version:
            logger.Logger.info(tag + "已查询到目标版本。")
            temp_1 = i
    if not temp_1:
        logger.Logger.warning(tag + "未查询到目标版本，查询失败。")
        return None

    version_type_ = temp_1["type"]
    version_type = version_type_dict[version_type_]

    version_url = temp_1["url"]

    version_update_time_ = temp_1["time"]
    version_update_time = time_translation(version_update_time_)

    version_release_time_ = temp_1["releaseTime"]
    version_release_time = time_translation(version_release_time_)

    logger.Logger.debug(tag + "列出查询结果。")
    logger.Logger.debug(tag + version)
    logger.Logger.debug(tag + version_url)
    logger.Logger.debug(tag + version_type)
    logger.Logger.debug(tag + version_update_time)
    logger.Logger.debug(tag + version_release_time)
    version_info_package = (version, version_url, version_type, version_update_time, version_release_time)
    logger.Logger.info(tag + "查询成功。")
    return version_info_package


def from_url_download_json(url: str, name: str, path: str, mode: str, sha1: str):
    """
    此函数仅支持从一个指向JSON的url里下载该JSON文件，并以指定的名字存储于指定位置。
    :param url: 指定JSON的url。
    :param name: 指定的保存名称，不包括文件后缀（自动添加后缀为.json）。
    :param path: 指定的保存地址，并不是该JSON文件预计的绝对路径。
    :param mode: 指定的下载方式，即Mojang，……
    :param sha1: [可选]如有sha1校验码，则在下载完毕后进行校验，若校验失败则自动重新下载，共尝试3次。
    :return: 如果下载成功则返回True，否则返回False。
    """
    circle_num = 1
    tag = random_num_tag()
    logger.Logger.info(tag + "即将开始以" + mode + "方式下载" + url + "，并保存至" + path + "，文件名为" + name + ".json。")
    if mode == "Mojang":
        download_url = url
    elif mode == "BMCLAPI":
        download_url = url.replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
    elif mode == "MCBBS":
        download_url = url.replace("https://launchermeta.mojang.com", "https://download.mcbbs.net")
    else:
        logger.Logger.warning(tag + "未知下载源，下载取消。")
        return False

    logger.Logger.info(tag + "下载源已确定，url转换成功：" + download_url)

    back_info = requests.get(download_url, timeout=20)

    if back_info.status_code == 200:
        logger.Logger.debug(tag + "下载源在线可用。")
    else:
        logger.Logger.warning(tag + "下载源响应不正常，下载取消。")
        return False

    if not os.path.isdir(path):
        logger.Logger.debug(tag + "指定的下载目录不存在，即将尝试创建该目录。")
        os.makedirs(name=path)
        logger.Logger.debug(tag + "创建目录成功。")
    else:
        logger.Logger.debug(tag + "指定目录已存在，无需创建新目录。")

    file_path = path + name + ".json"

    while circle_num < 4:
        try:
            with open(file=file_path, mode="w", encoding="utf-8") as file:
                logger.Logger.debug(tag + "已打开指定的文件，开始写入JSON数据。")
                file.write(back_info.text)
            logger.Logger.debug(tag + "数据写入完毕，文件已关闭。")
        except:
            logger.Logger.error(tag + "写入数据过程中出现错误，将在3秒后重试。")
            circle_num += 1
            time.sleep(3)

        if sha1:
            logger.Logger.debug(tag + "指定了预计的sha1校验码，开始验证下载文件的sha1。")
            logger.Logger.debug(tag + "指定了预计的sha1校验码：" + sha1)
            file = open(file=file_path, mode="rb")
            file_sha1 = hashlib.sha1(file.read()).hexdigest()
            file.close()
            logger.Logger.debug(tag + "下载文件计算得到的sha1：" + file_sha1)
            if file_sha1 == sha1:
                logger.Logger.info(tag + "验证完毕，下载无误。下载已结束！")
                circle_num = 4
            else:
                logger.Logger.warning(tag + "验证失败，文件下载似乎有误,将在三秒后重试。")
                circle_num += 1
                time.sleep(3)
        else:
            logger.Logger.info(tag + "未指定sha1校验码用于验证，下载已结束！")
            circle_num = 4

    return True


def from_url_download_jar(url: str, name: str, path: str, mode: str, sha1: str):
    """
    此函数仅支持从一个指向jar文件的url里下载该jar文件，并以指定的名字存储于指定位置。
    :param url: 指定jar文件的url。
    :param name: 指定的保存名称，不包括文件后缀（自动添加后缀为.jar）。
    :param path: 指定的保存地址，并不是该jar文件预计的绝对路径。
    :param mode: 指定的下载方式，即Mojang，……
    :param sha1: [可选]如有sha1校验码，则在下载完毕后进行校验，若校验失败则自动重新下载，连首次下载共尝试3次。
    :return: 如果下载成功则返回True，否则返回False。
    """
    circle_num = 1
    tag = random_num_tag()
    logger.Logger.debug(tag + "即将开始以" + mode + "方式下载" + url + "，并保存至" + path + "，文件名为" + name + ".jar。")
    if mode == "Mojang":
        download_url = url
    elif mode == "BMCLAPI":
        download_url = url.replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/maven")
    elif mode == "MCBBS":
        download_url = url.replace("https://libraries.minecraft.net", "https://download.mcbbs.net/maven")
    else:
        logger.Logger.warning(tag + "未知下载源，下载取消。")
        return False

    logger.Logger.debug(tag + "下载源已确定，url转换成功：" + download_url)

    if not os.path.isdir(path):
        logger.Logger.debug(tag + "指定的下载目录不存在，即将尝试创建该目录。")
        os.makedirs(name=path)
        logger.Logger.debug(tag + "创建目录成功。")
    else:
        logger.Logger.debug(tag + "指定目录已存在，无需创建新目录。")

    file_path = path + name + ".jar"

    while circle_num < 4:

        back_info = requests.get(download_url, stream=True, timeout=20)

        if back_info.status_code == 200:
            logger.Logger.debug(tag + "下载源在线可用。")
        else:
            logger.Logger.warning(tag + "下载源响应不正常，下载取消。")
            return False
        try:
            file = open(file=file_path, mode="wb+")
            logger.Logger.debug(tag + "已打开指定的文件，开始写入数据。")
            for chunk in back_info.iter_content(1024):
                if chunk:
                    file.write(chunk)
            file.close()
            logger.Logger.debug(tag + "数据写入完毕，文件已关闭。")
        except:
            logger.Logger.error(tag + "写入数据过程中出现错误，将在3秒后重试。")
            circle_num += 1
            time.sleep(3)
            continue

        if sha1:
            logger.Logger.debug(tag + "指定了预计的sha1校验码：" + sha1)
            file = open(file=file_path, mode="rb")
            file_sha1 = hashlib.sha1(file.read()).hexdigest()
            file.close()
            logger.Logger.debug(tag + "下载得到文件计算的sha1：" + file_sha1)
            if file_sha1 == sha1:
                logger.Logger.debug(tag + "验证完毕，下载无误。下载已结束！")
                circle_num = 4
            else:
                logger.Logger.warning(tag + "验证失败，文件下载似乎有误,将在三秒后重试。")
                circle_num += 1
                time.sleep(3)
        else:
            logger.Logger.debug(tag + "未指定sha1校验码用于验证，下载已结束！")
            circle_num = 4

    return True


def from_url_download_asset(url: str, name: str, path: str, mode: str):
    """
    此函数仅支持从一个指向文件的url里下载该文件，并以指定的名字存储于指定位置。（无后缀）
    :param url: 指定文件的url。
    :param name: 指定的保存名称，不包括文件后缀。
    :param path: 指定的保存地址，并不是该文件预计的绝对路径。
    :param mode: 指定的下载方式，即Mojang，……
    :return: 如果下载成功则返回True，否则返回False。
    """
    tag = random_num_tag()

    if mode == "Mojang":
        url_prefix = "https://resources.download.minecraft.net/"
    elif mode == "BMCLAPI":
        url_prefix = "https://bmclapi2.bangbang93.com/assets/"
    elif mode == "MCBBS":
        url_prefix = "https://download.mcbbs.net/assets/"
    else:
        logger.Logger.warning(tag + "未知下载源，下载取消。")
        return False

    real_url = url_prefix + url

    logger.Logger.debug(tag + "下载源已确定，url拼接成功：" + real_url)

    if not os.path.isdir(path):
        logger.Logger.debug(tag + "指定的下载目录不存在，即将尝试创建该目录。")
        os.makedirs(name=path)
        logger.Logger.debug(tag + "创建目录成功。")
    else:
        logger.Logger.debug(tag + "指定目录已存在，无需创建新目录。")

    file_path = path + name

    circle_num = 1

    while circle_num < 4:
        try:
            back_info = requests.get(real_url, stream=True, timeout=20)

            if back_info.status_code == 200:
                logger.Logger.debug(tag + "下载源在线可用。")
            else:
                logger.Logger.warning(tag + "下载源响应不正常，下载取消。")
                return False

            file = open(file=file_path, mode="wb+")
            logger.Logger.debug(tag + "已打开指定的文件，开始写入数据。")
            for chunk in back_info.iter_content(1024):
                if chunk:
                    file.write(chunk)
            file.close()
            logger.Logger.debug(tag + "数据写入完毕，文件已关闭。")
            break
        except:
            logger.Logger.error(tag + "下载过程中出现错误，将在3秒后重试。")
            circle_num += 1
            time.sleep(3)

    logger.Logger.debug(tag + "此次下载已结束。")


def time_translation(time_: str):
    time_list = time_.split("T")
    date = time_list[0]
    hour_list = time_list[1].split("+")
    hour = hour_list[0]
    time_output = date + " " + hour
    return time_output


def random_num_tag():
    num_tag = random.randint(0, 99999)
    str_tag = "[进度标志" + str(num_tag) + "]"
    return str_tag


